Implement the built-in
Exclude<T, U>
(Exclude from T those types that are assignable to U)
實現內建的 Exclude<T, U>
從 T 中排除那些可以賦值給 U 的類型
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
接下來,你的任務是讓下面的type cases測試通過:
type cases = [
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, 'b' | 'c'>>,
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, 'c'>>,
Expect<Equal<MyExclude<string | number | (() => void), Function>, string | number>>,
]
在這一關中,我們可以從以下幾個方向來思考:
T
和U
逐個比較?我們將會用到:
extends 在第三關已經介紹過,可以回到第三關參考喔!
解法:
type MyExclude<T, U> = T extends U ? never : T;
細節分析:
T extends U ? never : T
:
T extends U
:這部分檢查 T 中的每個成員是否可以賦值給 U。如果某個成員符合條件,則不包括該成員(返回 never),這樣最終的結果就只會包含不符合條件的成員。這樣,我們就能順利通過測試啦 🎉 😭 🎉
介紹:
“Distributive” :
An operation that produces the same result when applied to an entire expression as when applied individually to each part and then combining the results.
(such as multiplication in a(b + c) = ab + ac)
範例:
When conditional types act on a generic type, they become distributive when given a union type. For example, take the following:
type ToArray<Type> = Type extends any ? Type[] : never;
If we plug a union type into ToArray, then the conditional type will be applied to each member of that union.
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;
//^? type StrArrOrNumArr = string[] | number[]
What happens here is that ToArray distributes on:
string | number;
and maps over each member type of the union, to what is effectively:
ToArray<string> | ToArray<number>;
which leaves us with:
string[] | number[];
Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets.
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
// 'ArrOfStrOrNum' is no longer a union.
type ArrOfStrOrNum = ToArrayNonDist<string | number>;
//^? type ArrOfStrOrNum = (string | number)[]
Non-Distributive Conditional Types
- Tuple Type: By wrapping the type Type
in square brackets ([]
), you are creating a tuple type [Type]
.
- Non-Distributive: TypeScript no longer sees Type
as a naked type parameter. Instead, it sees [Type]
as a single entity, a tuple type containing Type
.
- Single Evaluation Since [Type]
is not a union, the conditional type extends [any]
is evaluated as a whole, not distributed over individual union members.
- Result The result is a non-distributive application of the conditional type, leading to the entire union type being treated as a single entity within the array.
本次介紹了 Exclude
的實作,下一關會挑戰 If
,期待再相見!